﻿using UnityEngine;
using System.Collections;
using Game;

public delegate void CallbackFunc();
public delegate void CallbackFuncWithInt(int param);
public delegate void CallbackFuncWithBool(bool param);
public delegate void CallbackFuncWithGameObj(GameObject param);
public delegate void CallbackFuncWithVector3(Vector3 param);
public delegate void CallbackFuncWithCollider(Collider param);

public delegate bool BoolCallbackFunc();
public delegate bool BoolCallbackFuncWithBool(bool param);
public delegate bool BoolCallbackFuncWithInt(int param);
public delegate bool BoolCallbackFuncWithStr(string param);
public delegate bool BoolCallbackFuncWithGameObj(GameObject param);

public delegate int IntCallbackFunc();
public delegate int IntCallbackFuncWithInt(int param);
public delegate int IntCallbackFuncWithFloat(float param);

/// <summary>
/// 物体转向枚举类型：
/// </summary>
public enum RAND_ROTATE_DIRECTION { NOPE, X_ROTATE, Y_ROTATE, X_Y_BOTH, Z_ROTATE };

/// <summary>
/// 提供各种游戏物体中常用的函数；
/// </summary>
public class GameObjFunc
{
    static MonoBehaviour _CoroutineMono;
    class EmptyMono : MonoBehaviour { }
    /// <summary>
    /// 用于帮助启动协程的实例（适用于没有继承MonoBehavior的情况）
    /// </summary>
   public static MonoBehaviour g_CoroutineInstance
    {
        get
        {
            if (_CoroutineMono == null)
            {
                GameObject _gObjInstance = new GameObject("CoroutineObj",typeof (EmptyMono));
                _CoroutineMono =  _gObjInstance.GetComponent<EmptyMono>();
            }

            return _CoroutineMono;
        }
    }

    #region 为游戏物体添加某些辅助类

    /// <summary>
    /// 获得一个空的物体，用于启动协程
    /// </summary>
    /// <returns></returns>
    public static MonoBehaviour GetEmptyGameObj()
    {
        return g_CoroutineInstance;
    }

    /// <summary>
    /// 启动协程，则无需依附于原物体
    /// </summary>
    /// <param name="routine"></param>
    /// <returns></returns>
    public static Coroutine StartCoroutine(IEnumerator routine)
    {
        return g_CoroutineInstance.StartCoroutine(routine);
    }

    /// <summary>
    /// 为游戏物体添加一个OnTrigger的监听
    /// </summary>
    /// <param name="srcObj"></param>
    /// <param name="onColliderEnter"></param>
    /// <param name="onColliderExit"></param>
    public static void AddGameObjTrigger(GameObject srcObj, CallbackFuncWithCollider onColliderEnter, CallbackFuncWithCollider onColliderExit = null)
    {
        var objTrigger = srcObj.AddComponent<GameObjTrigger>();
        objTrigger.Init(srcObj, onColliderEnter, onColliderExit);
    }

    #endregion

    /// <summary>
    /// 压扁物体
    /// </summary>
    /// <param name="scaleTargetTimes">缩放倍数</param>
    public static IEnumerator IScaleDownTrans(Transform m_transform, float scaleTargetTimes = 0.4F)
    {
        bool isShrink = scaleTargetTimes < 1F;                    //是否是缩小
        Vector3 currScale = m_transform.localScale;
        float currScaleY = currScale.y;
        float tarScaleY = currScaleY * scaleTargetTimes;
        float rateToScale = (currScaleY - tarScaleY);// *( isShrink ? 1F : -1F ); //是缩小的话，则缩小率为（当前-目标）
        //是放大的话，则为（目标-当前）

        while (((currScaleY > tarScaleY) && isShrink)              //缩小的情况下，则是在当前还比目标状态大的情况下进行缩小
            ||
            ((!isShrink) && currScaleY < tarScaleY)                //放大时，则是在当前还比target小的情况下进行缩大
            )
        {
            //缩小时减少，放大时增大
            currScaleY -= rateToScale * Time.deltaTime;// *( isShrink ? -1 : 1 );
            currScale.y = currScaleY;
            m_transform.localScale = currScale;
            yield return 1;
        }

        currScale.y = tarScaleY;
        m_transform.localScale = currScale;
    }

    internal static void AssetNotNull(object obj)
    {
#if UNITY_EDITOR || CHECK_NULL
        if(obj==null)
        {
            Debug.LogError("Parameter is null ");
        }
#endif
    }

    /// <summary>
    /// 判断是Null或者空长度
    /// </summary>
    /// <param name="collection"></param>
    /// <returns></returns>
    public static bool IsNullOrEmpty(ICollection collection)
    {
        return collection == null || collection.Count <= 0;
    }


    /// <summary>
    /// 判断是非空且有内容（Count>0）
    /// </summary>
    /// <param name="collection"></param>
    /// <returns></returns>
    public static bool HasItems(ICollection collection)
    {
        return collection != null && collection.Count > 0;
    }

    /// <summary>
    /// 打开或关闭一组物体
    /// </summary>
    /// <param name="objs"></param>
    /// <param name="isOn"></param>
    public static void TurnOnOffObj( MonoBehaviour[] objs,bool isOn)
    {
        if (!GameObjFunc.IsNullOrEmpty(objs))
        {
            foreach (var point in objs)
            {
                if (point != null)
                {
                    point.gameObject.SetActive(isOn);
                }
            }
        }
    }


    /// <summary>
    /// 打开或关闭一组物体
    /// </summary>
    /// <param name="objs"></param>
    /// <param name="isOn"></param>
    public static void TurnOnOffObj(GameObject[] objs, bool isOn, CallbackFuncWithGameObj onObjOperated=null)
    {
        if (!GameObjFunc.IsNullOrEmpty(objs))
        {
            foreach (var point in objs)
            {
                if (point != null)
                {
                    if(onObjOperated!=null)
                    {
                        onObjOperated(point);
                    }

                    point.SetActive(isOn);
                }
            }
        }
    }

    /// <summary>
    /// 直接让物体定位到一个物体的位置上
    /// </summary>
    /// <param name="deltaYToTarget">与目标物体Transform在Y轴上的偏移值</param>
    public static void FollowTrans(Transform src, Transform target, float deltaXToTarget = 0, float deltaYToTarget = 0)
    {
        //Follow position:
        Vector3 tmpV3 = Camera.main.WorldToScreenPoint(target.position);
        tmpV3.x -= deltaXToTarget;
        tmpV3.y -= deltaYToTarget;
        src.position = tmpV3;
    }

    /// <summary>
    /// 跟随到鼠标的位置
    /// </summary>
    /// <param name="src"></param>
    public static void FollowMouse(Transform src)
    {
        Vector3 mousePos = MyInput.mousePosition;
        Vector3 worldPos = Camera.main.ScreenToWorldPoint(mousePos);
        src.transform.position = worldPos;
    }

    /// <summary>
    /// 协程：移动一个物体到指定位置
    /// </summary>
    public static IEnumerator IMoveTransToPos(Transform srcRT, Vector3 targetPos, float moveTime = 1, CallbackFunc onDone = null)
    {

        float timePassed = 0F;
        float percent = 0;
        Vector3 v3Src = srcRT.position;
        while (timePassed <= moveTime)
        {
            srcRT.position = Vector3.Lerp(v3Src, targetPos, percent);

            timePassed += Time.deltaTime;
            percent = timePassed / moveTime;
            yield return 1;
        }

        srcRT.position = targetPos;

        if (onDone != null)
        {
            onDone();
        }
    }

    /// <summary>
    /// 发起一个持续一段时间的Update函数，每一帧更新
    /// </summary>
    /// <param name="continueTime">更新持续的时间</param>
    /// <param name="updateFunc">更新的动作</param>
    public static IEnumerator IUpdateDo(float continueTime, CallbackFunc updateFunc, CallbackFunc doneFunc = null)
    {
        float timePassed = 0F;

        while (timePassed <= continueTime)
        {
            updateFunc();

            timePassed += Time.deltaTime;
            yield return 1;
        }

        if (doneFunc != null)
            doneFunc();
    }

    /// <summary>
    /// 持续Update一段动作，同时指定了Update的时间间隔
    /// </summary>
    /// <param name="deltaTime">每次update间的间隔时间</param>
    public static IEnumerator IUpdateDo(float continueTime, float deltaTime, CallbackFunc updateFunc, CallbackFunc doneFunc = null)
    {
        float timePassed = 0F;
        WaitForSeconds waitTime = new WaitForSeconds(deltaTime);

        while (timePassed <= continueTime)
        {
            updateFunc();

            timePassed += deltaTime;
            yield return waitTime;
        }

        if (doneFunc != null)
            doneFunc();
    }

    /// <summary>
    /// 启动一个update协程，并且可以指定间隔时间，停止条件
    /// </summary>
    /// <param name="deltaTime">update 间隔时间</param>
	/// <param name="cbContinueCondition">继续更新的条件</param>
	public static IEnumerator IUpdateDo(CallbackFunc updateFunc, float deltaTime = 0, BoolCallbackFunc cbContinueCondition = null, CallbackFunc onDone=null)
    {
        //如果没有设置退出条件的回调函数，则设置回调函数为true造成死循环
        if (cbContinueCondition == null)
        {
            cbContinueCondition =
                () =>
            {
                return true;
            };

        }


        if (deltaTime > 0)
        {
            //有间隔更新
            WaitForSeconds waitSeconds = new WaitForSeconds(deltaTime);

            //Debug.Log ("deltatime:"+deltaTime);

            while (cbContinueCondition() == true)
            {
                updateFunc();

                yield return waitSeconds;
            }
        }
        else
        {
            //每一帧更新
            while (cbContinueCondition() == true)
            {
                updateFunc();

                yield return 1;
            }
        }

        if(onDone!=null)
        {
            onDone();
        }
    }

    /// <summary>
    /// 延迟一段时间才执行动作
    /// </summary>
    public static IEnumerator IDelayDoSth(CallbackFunc delayAction, float delayTime)
    {
        while (delayTime > 0)
        {
            yield return 1;
            delayTime -= Time.deltaTime;
        }

        delayAction();
    }

    /// <summary>
    /// 延迟一帧才执行动作
    /// </summary>
    public static IEnumerator IDelayDoSth(CallbackFunc delayAction)
    {
        yield return 0;

        delayAction();
    }

    /// <summary>
    /// 延迟直到条件满足才执行动作
    /// </summary>
    /// <param name="cb_delayAction">延迟执行的动作</param>
    /// <param name="cb_isSatisfied">当某个条件满足时才能执行</param>
    /// <param name="checkTime">每次检查条件的时间</param>
    public static IEnumerator IDelayDoSth(BoolCallbackFunc cb_isSatisfied, CallbackFunc cb_delayAction, float checkTime)
    {
        //如果没有设置退出条件的回调函数，或者时间为负，直接完成
        if (cb_isSatisfied == null || checkTime < 0)
        {
            Debug.LogError("Condition:" + cb_isSatisfied + " ,checkTime:" + checkTime);
            cb_delayAction();
        }

        //有间隔更新
        WaitForSeconds waitSeconds = new WaitForSeconds(checkTime);

        //Debug.Log ("deltatime:"+deltaTime);

        //--条件不满足时，不断等待：
        while (cb_isSatisfied() == false)
        {
            yield return waitSeconds;
        }

        cb_delayAction();
    }

    /// <summary>
    /// 为Transform设置随机的Rotation(选定的方向下0~360)
    /// </summary>
    public static void SetRandomRotation(Transform transform, RAND_ROTATE_DIRECTION randRotation)
    {
        #region 随机转向：
        if (randRotation != RAND_ROTATE_DIRECTION.NOPE)
        {
            //给出180/0的随机转向角度：
            float rotateX = 0;
            float rotateY = 0;
            float rotateZ = 0;

            //是否应该随机左右、上下颠倒
            switch (randRotation)
            {
                //Try to rotate it with scale revert.
                case RAND_ROTATE_DIRECTION.X_Y_BOTH:
                    //3D下直接Rotate
                    rotateX = Random.Range(0, 360);
                    rotateY = Random.Range(0, 360);
                    break;

                case RAND_ROTATE_DIRECTION.Z_ROTATE:
                    rotateZ = Random.Range(0, 360);
                    break;

                case RAND_ROTATE_DIRECTION.X_ROTATE:
                    rotateX = Random.Range(0, 360);                 //Rotate in X asix
                    break;

                case RAND_ROTATE_DIRECTION.Y_ROTATE:
                    rotateY = Random.Range(0, 360);                 //Rotate in Y asix
                    break;
            }

            //随机上下左右转向的话：
            transform.Rotate(rotateX, rotateY, rotateZ, Space.World);
        }
        #endregion

    }

    /// <summary>
    /// 设置Transform的范围内随机大小
    /// </summary>
    public static void SetRandomScale(Transform transform, float scaleMin, float scaleMax)
    {
        #region 随机大小范围
        float randScale = Random.Range(scaleMin, scaleMax);
        transform.localScale = new Vector3(randScale, randScale, randScale);
        #endregion
    }

    /// <summary>
    /// 转向目标物体，并且保持为只在y轴上的转向
    /// </summary>
    public static void RotateYToTransform(Transform src, Transform target)
    {
        RotateYToTransform(src, target.position);
    }

    /// <summary>
    /// 转向目标物体，并且保持为只在y轴上的转向
    /// </summary>
    public static void RotateYToTransform(Transform src, Vector3 target)
    {
        //转向老爹
        src.LookAt(target);
        Vector3 eulerRotate = src.rotation.eulerAngles;

        //修正欧拉角转向
        eulerRotate.x = eulerRotate.z = 0;
        src.rotation = Quaternion.Euler(eulerRotate);
    }


}
